package com.xishu.util;


import com.xishu.bo.TimeIntegerConfig;
import com.xishu.config.Config;
import com.xishu.entity.shop.SceneTimeConfig;
import com.xishu.entity.shop.TimeConfig;
import com.xishu.exception.ResponseStatusException;
import com.xishu.response.ResponseStatus;
import com.xishu.response.VerifyUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;

public class DateUtil {
    private static Logger logger = LoggerFactory.getLogger(DateUtil.class);
    public static final long ONE_SECONDS = 1000;

    public static final long ONE_MINUTE = ONE_SECONDS * 60;

    public static final long ONE_HOUR = ONE_MINUTE * 60;

    public static final long ONE_DAY = ONE_HOUR * 24;

    public static final long HALF_MONTH = ONE_DAY * 15;

    public static final long ONE_MONTH = ONE_DAY * 30;

    public static final long ONE_YEAR = ONE_DAY * 365;

    public static final int TIME_ZONE = 8;

    public static final int US_TIME_ZONE = -8;

    public static final Map<String, Integer> WEEK_DAY_MAP = new HashMap<>();
    public static final int DAY_TIME_INT = 10000;

    static {
        WEEK_DAY_MAP.put("Monday", 1);
        WEEK_DAY_MAP.put("Tuesday", 2);
        WEEK_DAY_MAP.put("Wednesday", 3);
        WEEK_DAY_MAP.put("Thursday", 4);
        WEEK_DAY_MAP.put("Friday", 5);
        WEEK_DAY_MAP.put("Saturday", 6);
        WEEK_DAY_MAP.put("Sunday", 7);
    }

    private static int getTimeZone() {
        if (Config.getInstance().isUsVersion()) {
            return US_TIME_ZONE;
        } else {
            return TIME_ZONE;
        }
    }

    /**
     * 获取时区偏移量
     *
     * @return
     */
    private static ZoneOffset getZoneOffSet() {
        //美国版本有夏令时，这个时候用JAVA提供的接口
        if (Config.getInstance().isUsVersion()) {
            ZoneId zoneId = ZoneId.of("America/Los_Angeles");
            Instant instant = Instant.now(); //can be LocalDateTime
            return zoneId.getRules().getOffset(instant);
        } else {
            return ZoneOffset.ofHours(TIME_ZONE);
        }
    }

    /**
     * 生成时间格式，精确到秒
     *
     * @param time
     * @return
     */
    public static String formatDate(long time) {
        if (time == 0l) {
            return "";
        }

        LocalDateTime time2 = LocalDateTime.ofEpochSecond(time / 1000, 0, getZoneOffSet());
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String format = df.format(time2);
        return format;
    }

    /**
     * 生成时间格式，精确到分
     *
     * @param time
     * @return
     */
    public static String formatDateMin(long time) {
        if (time == 0l) {
            return "";
        }

        LocalDateTime time2 = LocalDateTime.ofEpochSecond(time / 1000, 0, getZoneOffSet());
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm");
        String format = df.format(time2);
        return format;
    }




    /**
     * 生成时间格式，精确到天
     *
     * @param time
     * @return
     */
    public static String formatDayDate(long time) {
        if (time == 0) {
            return "";
        }

        LocalDateTime time2 = LocalDateTime.ofEpochSecond(time / 1000, 0, getZoneOffSet());
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        String format = df.format(time2);
        return format;
    }

    /**
     * 生成一周当中第几天
     *
     * @param time
     * @return
     */
    public static String formatWeekDay(long time) {
        if (time == 0) {
            return "";
        }

        LocalDateTime time2 = LocalDateTime.ofEpochSecond(time / 1000, 0, getZoneOffSet());
        DateTimeFormatter df = DateTimeFormatter.ofPattern("EEEE", Locale.ENGLISH);
//        DateTimeFormatter df = DateTimeFormatter.ofPattern("E");
        String format = df.format(time2);
        return format;
    }

    /**
     * 生成一周当中第几天
     *
     * @param time
     * @return
     */
    public static int getDayOfWeek(long time) throws ResponseStatusException {
        if (time == 0) {
            VerifyUtil.throwError(ResponseStatus.INVALID_PARAMETER);
        }

        LocalDateTime time2 = LocalDateTime.ofEpochSecond(time / 1000, 0, getZoneOffSet());
        DateTimeFormatter df = DateTimeFormatter.ofPattern("EEEE", Locale.ENGLISH);
        String format = df.format(time2);

        Integer dayOfWeek = WEEK_DAY_MAP.get(format);

        VerifyUtil.verify(() -> dayOfWeek != null);

        return dayOfWeek;
    }


    /**
     * 生成时间格式，精确到天
     *
     * @param time
     * @return
     */
    public static String format(long time, String formatStr) {
        if (time == 0) {
            return "";
        }

        LocalDateTime time2 = LocalDateTime.ofEpochSecond(time / 1000, 0, getZoneOffSet());
        DateTimeFormatter df = DateTimeFormatter.ofPattern(formatStr);
        String format = df.format(time2);
        return format;
    }

    /**
     * 生成时间格式，精确到天
     *
     * @param time
     * @return
     */
    public static String format_en(long time, String formatStr) {
        if (time == 0) {
            return "";
        }

        LocalDateTime time2 = LocalDateTime.ofEpochSecond(time / 1000, 0, getZoneOffSet());
        DateTimeFormatter df = DateTimeFormatter.ofPattern(formatStr, Locale.ENGLISH);
        String format = df.format(time2);
        return format;
    }


    /**
     * 生成时间格式，只要小时
     *
     * @param time
     * @return
     */
    public static String formatHourDate(long time) {
        if (time == 0) {
            return "";
        }

        LocalDateTime time2 = LocalDateTime.ofEpochSecond(time / 1000, 0, getZoneOffSet());
        DateTimeFormatter df = DateTimeFormatter.ofPattern("HH");
        String format = df.format(time2);
        return format;
    }

    /**
     * 生成时间格式，只要小时
     *
     * @param time
     * @return
     */
    public static String formatYearDate(long time) {
        if (time == 0) {
            return "";
        }

        LocalDateTime time2 = LocalDateTime.ofEpochSecond(time / 1000, 0, getZoneOffSet());
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy");
        String format = df.format(time2);
        return format;
    }

    /**
     * 生成月份的时间
     *
     * @param time
     * @return
     */
    public static String formatMonthDate(long time) {
        LocalDateTime time2 = LocalDateTime.ofEpochSecond(time / 1000, 0, getZoneOffSet());
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM");
        String format = df.format(time2);
        return format;
    }

    /**
     * 生成月份的时间
     *
     * @param time
     * @return
     */
    public static String formatOnlyMonthDate(long time) {
        LocalDateTime time2 = LocalDateTime.ofEpochSecond(time / 1000, 0, getZoneOffSet());
        DateTimeFormatter df = DateTimeFormatter.ofPattern("MM");
        String format = df.format(time2);
        return format;
    }

    /**
     * 获取当前时间
     *
     * @return
     */
    public static String currentTime() {
        long currentTimeMillis = System.currentTimeMillis();
        return formatDate(currentTimeMillis);
    }

    public static boolean isMoreThanOneYear(long startTime, long endTime) {
        return (endTime - startTime) > ONE_YEAR;
    }

    /**
     * 日期转毫秒
     *
     * @param year
     * @return
     */
    public static long dateToTime(int year, int month) {
        return dateToTime(year, month, 1);
    }

    /**
     * 日期转毫秒
     *
     * @param year
     * @return
     */
    public static long dateToTime(int year, int month, int daoOfMonth) {
        LocalDateTime localDateTime = LocalDateTime.of(year, month, daoOfMonth, 0, 0);
        return localDateTime.toEpochSecond(getZoneOffSet()) * 1000;
    }

    /**
     * 日期转毫秒
     *
     * @param year
     * @return
     */
    public static long dateToTime(int year, int month, int daoOfMonth, int hour, int minute) {
        LocalDateTime localDateTime = LocalDateTime.of(year, month, daoOfMonth, hour, minute);
        return localDateTime.toEpochSecond(getZoneOffSet()) * 1000;
    }

    /**
     * 日期转毫秒
     *
     * @param dateString
     * @return
     */
    public static long dateToTime(String dateString) {
        if (StringUtils.isEmpty(dateString)) {
            return 0l;
        }

        //如果是全数字的，也可以转
        try {
            Integer.parseInt(dateString);
            if (dateString.length() == 8) {
                String year = dateString.substring(0, 4);
                String month = dateString.substring(4, 6);
                String day = dateString.substring(6, 8);
                return dateToTime(Integer.parseInt(year), Integer.parseInt(month), Integer.parseInt(day));
            } else {
                return 0l;
            }
        } catch (Exception e) {

            //不是全数字
            String[] dateDay = dateString.split("-");

            if (dateDay.length == 0 || dateDay.length == 1) {
                return 0l;
            }

            if (dateDay.length == 2) {
                return dateToTime(Integer.parseInt(dateDay[0]), Integer.parseInt(dateDay[1]));
            }

            return dateToTime(Integer.parseInt(dateDay[0]), Integer.parseInt(dateDay[1]), Integer.parseInt(dateDay[2]));
        }
    }

    /**
     * 日期转毫秒
     *
     * @param dateString
     * @return
     */
    public static long excelDateToTime(String dateString) {
        String[] dateDay = dateString.split("/");

        if (dateDay.length == 0 || dateDay.length == 1) {
            return 0l;
        }

        if (dateDay.length == 2) {
            return dateToTime(Integer.parseInt(dateDay[0]), Integer.parseInt(dateDay[1]));
        }

        return dateToTime(Integer.parseInt(dateDay[0]), Integer.parseInt(dateDay[1]), Integer.parseInt(dateDay[2]));
    }

    /**
     * 一天的开始时间
     *
     * @return
     */
    public static long startTimeInDay(long currentTime) {
        String year = format(currentTime, "yyyy");
        String month = format(currentTime, "MM");
        String day = format(currentTime, "dd");
        return dateToTime(Integer.parseInt(year), Integer.parseInt(month), Integer.parseInt(day), 0, 0);
    }

    /**
     * 一天的结束时间
     *
     * @return
     */
    public static long endTimeInDay(long currentTime) {
        String today = format(currentTime, "yyyyMMdd");
        return dateToTime(today) + ONE_DAY - ONE_SECONDS;
    }

    /**
     * 给定时间是否在时间范围里面
     *
     * @param startTime
     * @param endTime
     * @param time
     * @return
     */
    public static boolean inTimeRange(String startTime, String endTime, long time) {
        int startInt = hourStringToInt(startTime);
        int endInt = hourStringToInt(endTime);

        String testTimeString = format(time, "HH:mm");
        int testTime = hourStringToInt(testTimeString);

        return testTime >= startInt && testTime <= endInt;
    }

    /**
     * 小时，分钟转数字
     *
     * @param time
     * @return
     */
    public static int hourStringToInt(String time) {
        String[] timeArray = time.split(":");
        return Integer.parseInt(timeArray[0]) * 100 + Integer.parseInt(timeArray[1]);
    }

    /**
     * 获取一周之后的下一天
     *
     * @param day
     * @return
     */
    public static int getNextWeekDay(int day) throws ResponseStatusException {
        if (day >= 1 && day <= 6) {
            return day + 1;
        } else if (day == 7)
            return 1;

        VerifyUtil.throwError(ResponseStatus.INVALID_PARAMETER);
        return 1;
    }

    /**
     * 是否有相同的时间范围交集
     *
     * @param timeConfigEnd
     * @param timeConfigStart
     * @return
     */
    public static boolean hasSameTime(TimeConfig timeConfigStart, TimeConfig timeConfigEnd) {
        logger.info("test start time {}", timeConfigStart);
        logger.info("test end time {}", timeConfigEnd);

        List<Integer> listEnd = timeConfigToHourInt(timeConfigEnd);
        List<Integer> listStart = timeConfigToHourInt(timeConfigStart);

        Integer startTime1 = listEnd.get(0);
        Integer endTime1 = listEnd.get(1);
        Integer startTime2 = listStart.get(0);
        Integer endTime2 = listStart.get(1);

        if (startTime1 >= startTime2 && startTime1 <= endTime2) {
            return true;
        }

        if (endTime1 >= startTime2 && endTime1 <= endTime2) {
            return true;
        }

        if (startTime2 >= startTime1 && startTime2 <= endTime1) {
            return true;
        }

        if (endTime2 >= startTime1 && endTime2 <= endTime1) {
            return true;
        }

        return false;
    }

    /**
     * 配置转数字
     *
     * @param timeConfig
     * @return
     */
    public static List<Integer> timeConfigToHourInt(TimeConfig timeConfig) {
        String startTime1 = timeConfig.getStartTime();
        int startHour1 = hourStringToInt(startTime1);
        String endTime1 = timeConfig.getEndTime();
        int endHour1 = hourStringToInt(endTime1);

        //结束时间更早，那么是第二天的，添加24小时
        if (endHour1 < startHour1) {
            endHour1 = endHour1 + 24 * 100;
        }

        ArrayList<Integer> list = new ArrayList<>();
        list.add(startHour1);
        list.add(endHour1);
        return list;
    }

    /**
     * 时间配置转换
     *
     * @param sceneTimeConfig
     * @return
     */
    public static List<TimeIntegerConfig> sceneTimeConfigToIntConfig(SceneTimeConfig sceneTimeConfig) {
        Integer day = sceneTimeConfig.getDay();
        String startTime = sceneTimeConfig.getStartTime();
        String endTime = sceneTimeConfig.getEndTime();
        int startTimeInt = hourStringToInt(startTime);
        int endTimeInt = hourStringToInt(endTime);

        //没有跨天
        if (endTimeInt >= startTimeInt) {
            TimeIntegerConfig timeIntegerConfig = new TimeIntegerConfig();
            timeIntegerConfig.setStartTime(day * DAY_TIME_INT + startTimeInt);
            timeIntegerConfig.setEndTime(day * DAY_TIME_INT + endTimeInt);
            return Arrays.asList(timeIntegerConfig);
        } else {
            //有跨天，那么需要特殊处理，特别是星期天的跨天，星期天的跨天，那么就是星期一了
            if (sceneTimeConfig.getDay() != 7) {
                TimeIntegerConfig timeIntegerConfig = new TimeIntegerConfig();
                timeIntegerConfig.setStartTime(day * DAY_TIME_INT + startTimeInt);
                timeIntegerConfig.setEndTime((day + 1) * DAY_TIME_INT + endTimeInt);
                return Arrays.asList(timeIntegerConfig);
            } else {
                List<TimeIntegerConfig> timeIntegerConfigArrayList = new ArrayList<>();
                TimeIntegerConfig timeIntegerConfigOne = new TimeIntegerConfig();
                timeIntegerConfigOne.setStartTime(day * DAY_TIME_INT + startTimeInt);
                timeIntegerConfigOne.setEndTime(day * DAY_TIME_INT + hourStringToInt("24:00"));
                timeIntegerConfigArrayList.add(timeIntegerConfigOne);

                TimeIntegerConfig timeIntegerConfig = new TimeIntegerConfig();
                timeIntegerConfig.setStartTime(1 * DAY_TIME_INT + hourStringToInt("00:00"));
                timeIntegerConfig.setEndTime(1 * DAY_TIME_INT + endTimeInt);
                timeIntegerConfigArrayList.add(timeIntegerConfig);

                return timeIntegerConfigArrayList;
            }
        }
    }

    /**
     * 两个时间段是否有重复部分，左开右闭
     *
     * @param timeIntegerConfig1
     * @param timeIntegerConfig2
     * @return
     */
    public static boolean hasSameTime(TimeIntegerConfig timeIntegerConfig1, TimeIntegerConfig timeIntegerConfig2) {
        int startTime = timeIntegerConfig1.getStartTime();
        int endTime = timeIntegerConfig1.getEndTime();

        //开始时间在集合里面
        if (startTime > timeIntegerConfig2.getStartTime() && startTime < timeIntegerConfig2.getEndTime()) {
            return true;
        }

        if (timeIntegerConfig2.getStartTime() > startTime && timeIntegerConfig2.getStartTime() < endTime) {
            return true;
        }

        if (endTime > timeIntegerConfig2.getStartTime() && endTime < timeIntegerConfig2.getEndTime()) {
            return true;
        }

        if (timeIntegerConfig2.getEndTime() > startTime && timeIntegerConfig2.getEndTime() < endTime) {
            return true;
        }

        if (endTime == timeIntegerConfig2.getEndTime()) {
            return true;
        }

        if (startTime == timeIntegerConfig2.getStartTime()) {
            return true;
        }

        return false;
    }

    /**
     * 判断两个判断是否有一样的时间
     *
     * @param sceneTimeConfig1
     * @param sceneTimeConfig2
     * @return
     */
    public static boolean hasSameTime(SceneTimeConfig sceneTimeConfig1, SceneTimeConfig sceneTimeConfig2) {
        List<TimeIntegerConfig> timeIntegerConfigList1 = sceneTimeConfigToIntConfig(sceneTimeConfig1);
        List<TimeIntegerConfig> timeIntegerConfigList2 = sceneTimeConfigToIntConfig(sceneTimeConfig2);

        for (TimeIntegerConfig timeIntegerConfig : timeIntegerConfigList1) {
            for (TimeIntegerConfig integerConfig2 : timeIntegerConfigList2) {
                if (hasSameTime(timeIntegerConfig, integerConfig2)) {
                    logger.info("has same time  {} with {}", timeIntegerConfig, integerConfig2);
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * 判断当前时间是否在配置的时间范围内
     *
     * @param timeConfig
     * @return
     */
    public static boolean isTimeInTimeConfig(TimeConfig timeConfig) {
        String currentTime = format(System.currentTimeMillis(), "HH:mm");
        int hourInt = hourStringToInt(currentTime);

        //如果开始时间小于结束时间，那么判断一个范围
        int startHour = hourStringToInt(timeConfig.getStartTime());
        int endHour = hourStringToInt(timeConfig.getEndTime());
        if (startHour < endHour) {
            return hourInt > startHour && hourInt < endHour;
        }

        //如果开始时间大于结束时间，那么判断开始时间到24：00，24：00到结束时间
        int hour24Int = hourStringToInt("24:00");
        int hour0Int = hourStringToInt("00:00");
        if (hourInt > startHour && hourInt < hour24Int) {
            return true;
        }

        return hourInt > hour0Int && hourInt < endHour;
    }

    /**
     * 判断该日期是否是该月的最后一天
     *
     * @param dateString 需要判断的日期
     * @return
     */
    public static boolean isLastDayOfMonth(String dateString) throws ParseException {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date = simpleDateFormat.parse(dateString);

        return isLastDayOfMonth(date);
    }


    /**
     * 判断该日期是否是该月的最后一天
     *
     * @param date 需要判断的日期
     * @return
     */
    public static boolean isLastDayOfMonth(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.DAY_OF_MONTH) == calendar
                .getActualMaximum(Calendar.DAY_OF_MONTH);
    }

    /**
     * 将字符串的时间转化成对应的格式
     *
     * @param pattern
     * @param strDate
     * @return
     */
    public static Date stringToDate(String pattern, String strDate) throws ParseException {
        DateFormat dateFormat = new SimpleDateFormat(pattern);
        Date date = dateFormat.parse(strDate);
        return date;
    }

    /**
     * 获取当前的月份
     * yyyy-MM-dd
     *
     * @return
     */
    public static int getCurrentMonth() {
        return getMonth(System.currentTimeMillis());
    }

    /**
     * 获取对应的月份
     *
     * @return
     */
    public static int getMonth(long time) {
        String mm = format(time, "MM");
        return Integer.parseInt(mm);
    }

    /**
     * 获取是哪一天
     *
     * @param time
     * @return
     */
    public static int getDay(Long time) {
        String dd = format(time, "dd");
        return Integer.parseInt(dd);
    }

    /**
     * 获取小时数
     *
     * @param time
     * @return
     */
    public static int getHour(Long time) {
        String dd = format(time, "HH");
        return Integer.parseInt(dd);
    }

    /**
     * 获取月份
     *
     * @param time
     * @return
     */
    public static int getYear(Long time) {
        String mm = format(time, "YYYY");
        return Integer.parseInt(mm);
    }

    /**
     * 获取当前的天数
     *
     * @return
     */
    public static int getCurrentDay() {
        long currentTimeMillis = System.currentTimeMillis();

        String mm = format(currentTimeMillis, "dd");
        return Integer.parseInt(mm);
    }

    /**
     * 获取当前星期几
     *
     * @return
     */
    public static int getCurrentDayOfWeek() throws Exception {
        long currentTimeMillis = System.currentTimeMillis();
        return getDayOfWeek(currentTimeMillis);
    }


    /**
     * 获取一年的第几周
     *
     * @param time
     * @return
     */
    public static int getWeekOfYear(long time) {
        Date date = new Date(time);
        Calendar calendar = Calendar.getInstance();
        calendar.setFirstDayOfWeek(Calendar.MONDAY);
        calendar.setTime(date);

        return calendar.get(Calendar.WEEK_OF_YEAR);
    }

    /**
     * 添加几个月
     *
     * @param date
     * @param number
     * @return
     */
    public static Date addMonth(Date date, int number) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(Calendar.MONTH, number);
        return c.getTime();
    }

    /**
     * 添加几年
     *
     * @param date
     * @param number
     * @return
     */
    public static Date addYear(Date date, int number) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(Calendar.YEAR, number);
        return c.getTime();
    }

    /**
     * 获取星期几的字符串描述
     *
     * @param day
     * @return
     */
    public static String getWeekDayString(int day) throws ResponseStatusException {
        VerifyUtil.verify(() -> day >= 1 && day <= 7);
        for (Map.Entry<String, Integer> entry : WEEK_DAY_MAP.entrySet()) {
            if (entry.getValue() == day) {
                return entry.getKey();
            }
        }

        VerifyUtil.throwError(ResponseStatus.SYSTEM_ERROR);
        return "";
    }
}

